﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;
using System.IO;

namespace ComTools
{
    public partial class frmLG220L : Form
    {
        public frmLG220L()
        {
            InitializeComponent();
            //
            logger = log4net.LogManager.GetLogger(this.GetType());
        }

        log4net.ILog logger = null;

        private void btnListenStart_Click(object sender, EventArgs e)
        {
            int port = 1883;
            if (!int.TryParse(txtSocketPort.Text.Trim(), out port))
            {
                MessageBox.Show("端口号非数字");
                return;
            }
            if (port <= 0 || port > 65535)
            {
                MessageBox.Show("端口号不正确");
                return;
            }
            //
            socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //socketServer.Bind(new IPEndPoint(IPAddress.Parse("0.0.0.0"), 1883));
            socketServer.Bind(new IPEndPoint(IPAddress.Any, 1883));
            socketServer.Listen(20);
            OutputLine("开始监听");
            if (asyncCallbackAccept == null)
            {
                asyncCallbackAccept = new AsyncCallback(RequestAccepted);
            }
            socketServer.BeginAccept(asyncCallbackAccept, socketServer);
            //
            IsSocketWorking = true;
        }

        private void btnListenStop_Click(object sender, EventArgs e)
        {
            if (IsSocketWorking && socketServer != null)
            {
                IsSocketWorking = false;
                //SocketInformation si = socketServer.DuplicateAndClose(System.Diagnostics.Process.GetCurrentProcess().Id);
                try
                {
                    ClearItems();
                    remoteLG220.Clear();
                    //
                    Socket closeSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    closeSocket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), int.Parse(txtSocketPort.Text)));
                    closeSocket.Send(closeCommand);
                    //
                    socketServer.Close();
                }
                catch (Exception exc)
                {
                    OutputLine(exc.Message);
                }
            }
        }

        bool IsSocketWorking = false;
        Socket socketServer = null;
        AsyncCallback asyncCallbackAccept = null;
        Dictionary<string, LG220> remoteLG220 = new Dictionary<string, LG220>();
        byte[] closeCommand = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

        private void RequestAccepted(IAsyncResult asyncResult)
        {
            if (!IsSocketWorking)
            {
                OutputLine("已停止Socket连接");
                return;
            }
            try
            {
                Socket socket = asyncResult.AsyncState as Socket;
                Socket socketIn = socket.EndAccept(asyncResult);
                OutputLine($"接收到呼入的连接:{socketIn.RemoteEndPoint}");
                //
                if (!remoteLG220.ContainsKey(socketIn.RemoteEndPoint.ToString()))
                {
                    string gatewayid = GetGatewayId(socketIn.RemoteEndPoint.ToString());
                    LG220 lg = new LG220()
                    {
                        GatewayId = gatewayid,
                        Socket = socketIn,
                        NodeIds = new List<L101>(),
                    };
                    InitNodeIds(lg);
                    remoteLG220.Add(socketIn.RemoteEndPoint.ToString(), lg);
                    AddItems(socketIn.RemoteEndPoint.ToString());
                    Thread thread = new Thread(new ParameterizedThreadStart(DealSocketIn));
                    thread.Start(socketIn);
                }
                socket.BeginAccept(asyncCallbackAccept, socket);
            }
            catch (Exception exc)
            {
                OutputLine(exc.Message);
            }
        }

        private void DealSocketIn(object socket)
        {
            if (!IsSocketWorking)
            {
                return;
            }
            Socket socketIn = socket as Socket;
            EndPoint remoteEndPoint = socketIn.RemoteEndPoint;
            byte[] data = new byte[1024];
            //
            socketIn.BeginReceive(data, 0, data.Length, SocketFlags.None, new AsyncCallback((asyncResult) =>
            {
                if (!IsSocketWorking) { return; }
                SocketError socketError;
                int len = 0;
                try
                {
                    len = socketIn.EndReceive(asyncResult, out socketError);
                }
                catch (SocketException e)
                {
                    OutputLine(e.Message);
                    socketIn.Close();
                    return;
                }
                if (len == 0)
                {
                    return;
                }
                byte[] realdata = new byte[len];
                Array.Copy(data, realdata, len);
                //
                TaskQueue.Enqueue(new MethodInvoker(() =>
                {
                    DealReceiveData(remoteEndPoint, realdata);
                }));
                StartQueue();
                //
                if (IsSocketWorking)
                {
                    DealSocketIn(socket);
                }
                //
            }), null);
        }

        volatile Queue<MethodInvoker> TaskQueue = new Queue<MethodInvoker>();
        volatile bool TaskRunning = false;

        private void StartQueue()
        {
            if (TaskRunning) { return; }
            TaskRunning = true;
            Thread thread = new Thread(new ThreadStart(() =>
            {
                while (TaskQueue.Count > 0)
                {
                    var mi = TaskQueue.Dequeue();
                    this.Invoke(mi);
                }
                TaskRunning = false;
            }));
            thread.Start();
        }

        private void DealReceiveData(EndPoint endPoint, byte[] data)
        {
            //
            List<LG220Return> lg220Return = GetLG220Returns(data);
            //
            string remoteIP = endPoint.ToString();
            LG220 lg220 = remoteLG220[remoteIP];
            for (int i = 0; i < lg220Return.Count; i++)
            {
                LG220Return mod = lg220Return[i];
                if (string.IsNullOrEmpty(lg220.GatewayId))
                {
                    lg220.GatewayId = mod.GatewayId;
                }
                if (!lg220.NodeIds.Exists(x => x.NodeId.Equals(mod.NodeId, StringComparison.OrdinalIgnoreCase)))
                {
                    lg220.NodeIds.Add(new L101()
                    {
                        NodeId = mod.NodeId,
                        NodeIdShort = mod.NodeIdShort,
                    });
                }
                //
                mod = UpdLG220Return(mod);
                //
                if (mod.Command == "01" && IsHideNoData())
                {
                    //隐藏上线通知帧
                    continue;
                }
                if (mod.IsImage())
                {
                    LG220ReturnImage imageMode = mod as LG220ReturnImage;

                    if (imageMode.IsFirstImage())
                    {
                        if (HasGetFrameOneStarted)
                        {
                            autoResetEvent.Set();
                            HasGetFrameOneStarted = false;
                        }
                        LG220ReturnImageFirst firstImg = imageMode as LG220ReturnImageFirst;
                        SetTextBox(txtImgCrc32, firstImg.ImageCRC32);
                        int size = int.Parse(firstImg.ImageByteNums, System.Globalization.NumberStyles.HexNumber);
                        SetTextBox(txtImgSize, size.ToString());
                        SetTextBox(txtImgResult, firstImg.DigitHuman);
                        SetTextBox(txtTailFrameSN, firstImg.TailFrameSN);
                        int lastSN = int.Parse(firstImg.TailFrameSN, System.Globalization.NumberStyles.HexNumber);
                        SetTextBox(nudMaxFrameNo, lastSN);
                        OutputLine(firstImg.ToString());
                        OutputLine2(Newtonsoft.Json.JsonConvert.SerializeObject(firstImg));
                    }
                    else
                    {
                        OutputLine(imageMode.ToString());
                        OutputLine2(Newtonsoft.Json.JsonConvert.SerializeObject(imageMode));
                        //
                        LG220Image imageObject = DicGatewayNodeImage[mod.GatewayId][mod.NodeId];
                        if (imageObject.IsAllFrameReceived())
                        {
                            if (imageObject.IsImageFramesOK())
                            {
                                DateTime now = DateTime.Now;
                                string path = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures, Environment.SpecialFolderOption.None);
                                string file = System.IO.Path.Combine(path, imageObject.FirstFrame.GatewayId
                                    + "-" + imageObject.FirstFrame.NodeId
                                    + "-" + now.ToString("yyyyMMdd_HHmmss_fff") + ".png");
                                System.IO.File.WriteAllBytes(file, imageObject.ImageBytes);
                                System.IO.File.AppendAllText("detect_result.txt",
                                    $"{file}\t{imageObject.FirstFrame.DigitHuman}\t{now.ToString("yyyy-MM-dd HH:mm:ss.fff")}\r\n",
                                    Encoding.UTF8);
                                OutputLine("图片保存完毕:" + file);
                            }
                        }
                    }
                    if (HasGetFrameNStarted)
                    {
                        if (imageMode.FrameSN == CurrentSNHex && autoResetEvent != null)
                        {
                            autoResetEvent.Set();
                        }
                    }
                }
                else
                {
                    if (HasPhotographyStarted && mod.IsPhotography())
                    {
                        HasPhotographyStarted = false;
                        autoResetEvent.Set();
                    }
                    if (HasPhotoQueryStarted && mod.IsPhotoQuery())
                    {
                        if (mod.IsPhotoQueryOK())
                            HasPhotoQueryStarted = false;
                        autoResetEvent.Set();
                    }
                    if (HasPhotoDetectStarted && mod.IsPhotoDetect())
                    {
                        HasPhotoDetectStarted = false;
                        autoResetEvent.Set();
                    }
                    if (HasDetectQueryStarted && mod.IsDetectQuery())
                    {
                        if (mod.IsDetectQueryOK())
                            HasDetectQueryStarted = false;
                        autoResetEvent.Set();
                    }
                    OutputLine(mod.ToString());
                    OutputLine2(Newtonsoft.Json.JsonConvert.SerializeObject(mod));
                }
            }
            //
        }

        private List<LG220Return> GetLG220Returns(byte[] data)
        {
            List<LG220Return> list = new List<LG220Return>();
            if (data.Length < 27)
                goto runend;
            //
            LG220Return lg220Return = new LG220Return();
            list.Add(lg220Return);
            //
            lg220Return.Version = BitConverter.ToString(data, 0, 1).Replace("-", "");
            lg220Return.Command = BitConverter.ToString(data, 1, 1).Replace("-", "");
            lg220Return.GatewayId = BitConverter.ToString(data, 2, 4).Replace("-", "");
            lg220Return.NodeId = BitConverter.ToString(data, 6, 4).Replace("-", "");
            lg220Return.NodeIdShort = BitConverter.ToString(data, 10, 2).Replace("-", "");
            lg220Return.Tunnel = BitConverter.ToString(data, 12, 1).Replace("-", "");
            lg220Return.SNR = BitConverter.ToString(data, 13, 1).Replace("-", "");
            lg220Return.RSSI = BitConverter.ToString(data, 14, 2).Replace("-", "");
            lg220Return.NC = BitConverter.ToString(data, 16, 2).Replace("-", "");
            lg220Return.Timestamp = BitConverter.ToString(data, 18, 4).Replace("-", "");
            lg220Return.TerminalState = BitConverter.ToString(data, 22, 1).Replace("-", "");
            lg220Return.TerminalNums = BitConverter.ToString(data, 23, 2).Replace("-", "");
            lg220Return.DataLength = BitConverter.ToString(data, 25, 2).Replace("-", "");
            if (data.Length > 27) //(lg220Return.DataLength != "0000")
            {
                int i = Convert.ToInt32(lg220Return.DataLength, 16);
                if (i > 0)
                {
                    lg220Return.DataContent = BitConverter.ToString(data, 27, i).Replace("-", "");
                    //
                    int len = lg220Return.DataContent.Length;
                    if (len >= 2)
                    {
                        lg220Return.DiyProtecol = lg220Return.DataContent.Substring(0, 2);
                    }
                    if (len >= 4)
                    {
                        lg220Return.DiyDataLen = lg220Return.DataContent.Substring(2, 2);
                    }
                    if (len >= 8)
                    {
                        lg220Return.DiyCRC = lg220Return.DataContent.Substring(len - 4);
                    }
                    if (len > 8)
                    {
                        lg220Return.DiyCommand = lg220Return.DataContent.Substring(4, len - 8);
                        lg220Return.DiyCommandName = lg220Return.GetDiyCommName();
                    }
                }
                else
                {
                    lg220Return.DataContent = string.Empty;
                    lg220Return.DiyProtecol = string.Empty;
                    lg220Return.DiyDataLen = string.Empty;
                    lg220Return.DiyCommand = string.Empty;
                    lg220Return.DiyCRC = string.Empty;
                }
                //检查CRC校验数据完整性
                string crc = CRC.crc16_ccitt_xmodem_hostorder(lg220Return.DiyProtecol + lg220Return.DiyDataLen + lg220Return.DiyCommand);
                if (crc != lg220Return.DiyCRC)
                {
                    list.Remove(lg220Return);
                }
                //尾部是否有剩余
                int leftLen = data.Length - 27 - i;
                if (leftLen > 0)
                {
                    //如果还有剩余长度，递归
                    int startPos = 27 + i;
                    byte[] ccc = new byte[leftLen];
                    Array.Copy(data, startPos, ccc, 0, leftLen);
                    var r = GetLG220Returns(ccc);
                    list.AddRange(r);
                }
            }
            goto runend;
        runend:
            return list;
        }

        private LG220Return UpdLG220Return(LG220Return mod)
        {
            //
            if (mod.IsImage())
            {
                if (!DicGatewayNodeImage.ContainsKey(mod.GatewayId))
                {
                    DicGatewayNodeImage.Add(mod.GatewayId, new Dictionary<string, LG220Image>());
                }
                Dictionary<string, LG220Image> temp1 = DicGatewayNodeImage[mod.GatewayId];
                if (!temp1.ContainsKey(mod.NodeId))
                {
                    temp1.Add(mod.NodeId, new LG220Image() { OtherFrames = new List<LG220ReturnImage>() });
                }
                LG220Image dataImage = temp1[mod.NodeId];
                LG220ReturnImage modImage = new LG220ReturnImage(mod);
                if (modImage.IsFirstImage())
                {
                    dataImage.FirstFrame = new LG220ReturnImageFirst(modImage);
                    dataImage.OtherFrames.Clear();
                    return dataImage.FirstFrame;
                }
                else
                {
                    if (dataImage == null || dataImage.FirstFrame == null)
                        return mod;
                    //超出的不再加入，未超出时才加入
                    int lastSN = int.Parse(dataImage.FirstFrame.TailFrameSN, System.Globalization.NumberStyles.HexNumber);
                    int nowSN = int.Parse(modImage.FrameSN, System.Globalization.NumberStyles.HexNumber);
                    if (nowSN <= lastSN)
                    {
                        //已存在的不再加入，不存在时才加入
                        if (!dataImage.OtherFrames.Exists(x => x.FrameSN == modImage.FrameSN))
                        {
                            dataImage.OtherFrames.Add(modImage);
                        }
                    }
                    return modImage;
                }
            }
            else
            {
                return mod;
            }
        }

        Dictionary<string, Dictionary<string, LG220Image>> DicGatewayNodeImage = new Dictionary<string, Dictionary<string, LG220Image>>();

        private string GetGatewayId(string ipendpoint)
        {
            DataTable table = dataGridView3.DataSource as DataTable;
            DataRow[] dra = table.Select("IPAddress='" + ipendpoint + "'");
            if (dra.Length > 0)
            {
                return dra[0]["LoraCode"].ToString();
            }
            else
            {
                return string.Empty;
            }
        }

        private void InitNodeIds(LG220 lg220)
        {
            DataTable table = dataGridView2.DataSource as DataTable;
            DataRow[] dra = table.Select("loraCode='" + lg220.GatewayId + "'");
            if (dra.Length > 0)
            {
                lg220.NodeIds.Clear();
                //
                foreach (DataRow dr in dra)
                {
                    lg220.NodeIds.Add(new L101()
                    {
                        NodeId = dr["NodeId"].ToString(),
                        NodeIdShort = dr["NodeIdShort"].ToString()
                    });
                }
            }
        }

        #region 日志输出1

        Action<string> handlerOutput = null;

        private void Output(string msg)
        {
            if (handlerOutput == null)
            {
                handlerOutput = new Action<string>(Output2Rtb);
            }
            if (this.InvokeRequired)
            {
                this.BeginInvoke(handlerOutput, msg);
            }
            else
            {
                Output2Rtb(msg);
            }
        }

        private void Output2Rtb(string txt)
        {
            logger.Info(txt);
            richTextBox1.AppendText(txt);
            if (richTextBox1.Lines.Length > 1000)
            {
                richTextBox1.Clear();
            }
            if (cbxAutoScrollBottom1.Checked)
            {
                richTextBox1.SelectionStart = richTextBox1.TextLength;
                richTextBox1.ScrollToCaret();
            }
        }

        private void OutputLine(string msg)
        {
            Output(msg + Environment.NewLine);
        }

        #endregion

        #region 日志输出2

        Action<string> handlerOutput2 = null;

        private void Output2(string msg)
        {
            if (handlerOutput2 == null)
            {
                handlerOutput2 = new Action<string>(Output2Rtb2);
            }
            if (this.InvokeRequired)
            {
                this.BeginInvoke(handlerOutput2, msg);
            }
            else
            {
                Output2Rtb2(msg);
            }
        }

        private void Output2Rtb2(string txt)
        {
            richTextBox2.AppendText(txt);
            if (richTextBox2.Lines.Length > 1000)
            {
                richTextBox2.Clear();
            }
            if (cbxAutoScrollBottom2.Checked)
            {
                richTextBox2.SelectionStart = richTextBox2.TextLength;
                richTextBox2.ScrollToCaret();
            }
        }

        private void OutputLine2(string msg)
        {
            Output2(msg + Environment.NewLine);
        }

        #endregion

        #region 添加获取ComboBox1

        Action<string> handlerAddItems = null;

        private void AddItems(string item)
        {
            if (handlerAddItems == null)
            {
                handlerAddItems = new Action<string>((txxt) =>
                {
                    if (!cbxLG220.Items.Contains(txxt))
                    {
                        cbxLG220.Items.Add(txxt);
                    }
                });
            }
            this.Invoke(handlerAddItems, item);
        }

        Func<string> handlerGetItem = null;

        private string GetItem()
        {
            if (handlerGetItem == null)
            {
                handlerGetItem = new Func<string>(() =>
                {
                    if (cbxLG220.SelectedIndex == -1)
                    {
                        return string.Empty;
                    }
                    else
                    {
                        return cbxLG220.SelectedItem.ToString();
                    }
                });
            }
            object obj = this.Invoke(handlerGetItem);
            return obj.ToString();
        }

        Func<LG220> handlerGetLG220 = null;

        private LG220 GetLG220()
        {
            if (handlerGetLG220 == null)
            {
                handlerGetLG220 = new Func<LG220>(() =>
                {
                    if (cbxLG220.SelectedIndex == -1)
                    {
                        return null;
                    }
                    else
                    {
                        return cbxLG220.SelectedItem as LG220;
                    }
                });
            }
            object obj = this.Invoke(handlerGetLG220);
            return obj as LG220;
        }

        MethodInvoker miClearItem = null;
        MethodInvoker miClearItem2 = null;

        private void ClearItems()
        {
            if (miClearItem == null)
            {
                miClearItem = new MethodInvoker(() =>
                {
                    cbxLG220.Items.Clear();
                });
            }
            cbxLG220.Invoke(miClearItem);
            if (miClearItem2 == null)
            {
                miClearItem2 = new MethodInvoker(() =>
                {
                    cbxL101.Items.Clear();
                });
            }
            cbxL101.Invoke(miClearItem2);
        }

        #endregion

        #region 添加获取ComboBox2

        Action<string> handlerAddNodeId = null;

        private void AddNodeId(string nodeid)
        {
            if (handlerAddItems == null)
            {
                handlerAddNodeId = new Action<string>((txxt) =>
                {
                    if (!cbxL101.Items.Contains(txxt))
                    {
                        cbxL101.Items.Add(txxt);
                    }
                });
            }
            if (cbxL101.InvokeRequired)
            {
                cbxL101.Invoke(handlerAddNodeId, nodeid);
            }
            else
            {
                if (!cbxL101.Items.Contains(nodeid))
                {
                    cbxL101.Items.Add(nodeid);
                }
            }
        }

        Func<string> handlerGetNodeId = null;

        private string GetNodeId()
        {
            if (handlerGetNodeId == null)
            {
                handlerGetNodeId = new Func<string>(() =>
                {
                    if (cbxL101.SelectedIndex == -1)
                    {
                        return string.Empty;
                    }
                    else
                    {
                        return cbxL101.SelectedItem.ToString();
                    }
                });
            }
            object obj = this.Invoke(handlerGetNodeId);
            return obj.ToString();
        }

        #endregion

        #region 获取隐藏心跳包

        Func<bool> handlerGetBool = null;

        public bool IsHideNoData()
        {
            if (handlerGetBool == null)
            {
                handlerGetBool = new Func<bool>(() =>
                {
                    return cbxHideNoData.Checked;
                });
            }
            if (cbxHideNoData.InvokeRequired)
            {
                object obj = cbxHideNoData.Invoke(handlerGetBool);
                return (bool)obj;
            }
            else
            {
                return cbxHideNoData.Checked;
            }
        }

        #endregion

        #region 设置首帧内容

        Action<TextBox, string> HandlerSetTxt = null;

        private void SetTextBox(TextBox tb1, string txt)
        {
            if (HandlerSetTxt == null)
            {
                HandlerSetTxt = new Action<TextBox, string>((tb, msg) =>
                {
                    tb.Text = msg;
                });
            }
            tb1.Invoke(HandlerSetTxt, tb1, txt);
        }

        Action<NumericUpDown, int> HandlerSetNud = null;

        private void SetTextBox(NumericUpDown nud, int value)
        {
            if (HandlerSetNud == null)
            {
                HandlerSetNud = new Action<NumericUpDown, int>((control, dvalue) =>
                {
                    control.Maximum = dvalue;
                    control.Minimum = 2;
                    control.Value = 2;
                });
            }
            nud.Invoke(HandlerSetNud, nud, value);
        }

        #endregion

        private void btnCRC16Xmodem_Click(object sender, EventArgs e)
        {
            string hex16 = CRC.crc16_ccitt_xmodem_hostorder(textBox1.Text);
            OutputLine(hex16);
        }

        private void frmLLG220L_Load(object sender, EventArgs e)
        {
            InitIP();
            InitLG();
            InitOther();
        }

        private void InitLG()
        {
            DataTable table = new DataTable();
            table.Columns.Add("LoraCode");
            table.Columns.Add("NodeId");
            table.Columns.Add("NodeIdShort");
            //
            table.Rows.Add("25AB8D14", "01020001", "0001");
            table.Rows.Add("25AB8D14", "01020002", "0002");
            table.Rows.Add("25AB8D14", "01020003", "0003");
            //table.Rows.Add("25B4347A", "01020004", "0004");
            table.Rows.Add("25AB8D14", "01020004", "0004");
            table.Rows.Add("25AB8D14", "01020005", "0005");
            table.Rows.Add("25AB8D14", "01020016", "0016");
            table.Rows.Add("25AB8D14", "01020030", "0030");
            //
            table.AcceptChanges();
            //
            dataGridView2.DataSource = table;
            Application.DoEvents();
        }

        private void InitIP()
        {
            DataTable table = new DataTable();
            table.Columns.Add("IPAddress");
            table.Columns.Add("LoraCode");
            //
            //table.Rows.Add("10.0.10.130:1234", "25B4347A");
            table.Rows.Add("10.0.10.129:1234", "25AB8D14");
            //
            table.AcceptChanges();
            //
            dataGridView3.DataSource = table;
            Application.DoEvents();
        }

        private void InitOther()
        {
            cbxSleepUnit.SelectedIndex = 0;
            cbxAutoScrollBottom1.Checked = true;
            cbxAutoScrollBottom2.Checked = true;
        }

        private void SendCommand(object sender, string hexTail)
        {
            Control control = sender as Control;
            string item = GetItem();
            if (string.IsNullOrEmpty(item))
            {
                MessageBox.Show("首先选择要查询的网关IP");
                return;
            }
            string nodeid = GetNodeId();
            Socket socket = remoteLG220[item].Socket;
            string gatewayid = remoteLG220[item].GatewayId;
            if (string.IsNullOrEmpty(nodeid))
            {
                if (remoteLG220[item].NodeIds.Count > 0)
                {
                    nodeid = remoteLG220[item].NodeIds[0].NodeId;
                }
                else
                {
                    nodeid = "01020001";
                }
            }
            //
            string hex = GetCommand(gatewayid, nodeid, hexTail);
            byte[] data = CRC.StringToHexByte(hex);
            int len = socket.Send(data);
            //
            OutputLine(control.Text + ":" + hex + " 共发出" + len.ToString());
        }

        private void SendCommand(Socket socket, string gatewayid, string nodeid, string hexTail, Action<string> aftAction)
        {
            string hex = GetCommand(gatewayid, nodeid, hexTail);
            byte[] data = CRC.StringToHexByte(hex);
            socket.Send(data);
            if (aftAction != null)
            {
                aftAction(hex);
            }
        }

        private string GetCommand(string gatewayid, string nodeid, string hexTail)
        {
            //
            string crc = CRC.crc16_ccitt_xmodem_hostorder(hexTail);
            int len = (hexTail + crc).Replace("-", "").Replace(" ", "").Length / 2;
            string hexLen = Convert.ToString(len, 16).PadLeft(2, '0');
            //
            string hex = gatewayid + "-" + nodeid + "-" + hexLen + "-" + hexTail + "-" + crc;
            //
            return hex;
        }

        private void btnQueryStatus_Click(object sender, EventArgs e)
        {
            //SendCommand(sender, "05-010101-2014");
            SendCommand(sender, "010101");
        }

        private void btnOpenAllLight_Click(object sender, EventArgs e)
        {
            //SendCommand(sender, "06-11020101-6320");
            SendCommand(sender, "11020101");
        }

        private void btnCloseAllLight_Click(object sender, EventArgs e)
        {
            //SendCommand(sender, "06-11020104-C670");
            SendCommand(sender, "11020104");
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (cbxLG220.SelectedIndex > -1)
            {
                string remoteIp = cbxLG220.SelectedItem.ToString();
                LG220 lg = remoteLG220[remoteIp];
                if (lg != null)
                {
                    foreach (L101 l in lg.NodeIds)
                    {
                        AddNodeId(l.NodeId);
                    }
                }
            }
        }

        private void btnOpenSingleLight_Click(object sender, EventArgs e)
        {
            //SendCommand(sender, "06-02020101-18A0");
            SendCommand(sender, "02020101");
        }

        private void btnCloseSingleLight_Click(object sender, EventArgs e)
        {
            //SendCommand(sender, "06-02020104-BDF0");
            SendCommand(sender, "02020104");
        }

        private void btnLetL101Sleep_Click(object sender, EventArgs e)
        {
            SendCommand(sender, "02020600");
        }

        private void btnSetSleepInterval_Click(object sender, EventArgs e)
        {
            byte v = (byte)nudSleepTime.Value;
            string hex = string.Empty;
            var unit = cbxSleepUnit.SelectedItem.ToString();
            switch (unit)
            {
                case "MINUTE":
                    if (v < 1) { v = 1; }
                    if (v > 55) { v = 55; }
                    v = (byte)(v + 200);
                    break;
                case "DAY":
                    break;
            }
            hex = Convert.ToString(v, 16).PadLeft(2, '0');
            SendCommand(sender, "020204" + hex);
        }

        private void btnPhotographySingle_Click(object sender, EventArgs e)
        {
            SendCommand(sender, "02020201");
        }

        private void btnPhotographySingle2_Click(object sender, EventArgs e)
        {
            SendCommand(sender, "02020202");
        }

        private void btnCameraStart_Click(object sender, EventArgs e)
        {
            //开始拍照
            SendCommand(sender, "02020201");
        }

        private void btnCameraQuery_Click(object sender, EventArgs e)
        {
            //查询拍照结果
            SendCommand(sender, "010102");
        }

        private void btnImageDetect_Click(object sender, EventArgs e)
        {
            //开始图像识别
            SendCommand(sender, "02020300");
        }

        private void btnImageDetectQuery_Click(object sender, EventArgs e)
        {
            //查询识别结果
            SendCommand(sender, "010103");
        }

        private void btnGetImagePackage1_Click(object sender, EventArgs e)
        {
            //查询数据包第1帧内容
            SendCommand(sender, "040512-00000001");
        }

        private void btnGetImageXPackage_Click(object sender, EventArgs e)
        {
            int n = (int)nudMaxFrameNo.Value;
            string hex = Convert.ToString(n, 16).PadLeft(8, '0').ToUpper();
            SendCommand(sender, "040512-" + hex);
        }

        private void frmLLG220L_FormClosing(object sender, FormClosingEventArgs e)
        {
            btnListenStop.PerformClick();
        }

        private void btnOpenImageDir_Click(object sender, EventArgs e)
        {
            Process.Start(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures));
        }

        private volatile AutoResetEvent autoResetEvent = null;
        /// <summary>
        /// 开始连续获取第N帧图片字节
        /// </summary>
        private volatile bool HasGetFrameNStarted = false;
        /// <summary>
        /// 当前期望得到的帧的16进制序号
        /// </summary>
        private volatile string CurrentSNHex = string.Empty;

        private void btnGetImg_Click(object sender, EventArgs e)
        {
            if (autoResetEvent != null)
            {
                autoResetEvent.Close();
            }
            autoResetEvent = new AutoResetEvent(false);
            int start = 2;
            int end = (int)nudMaxFrameNo.Maximum;

            Thread t1 = new Thread(new ThreadStart(() =>
            {
                HasGetFrameNStarted = true;
                for (int i = start; i <= end; i++)
                {
                    string hex = Convert.ToString(i, 16).PadLeft(8, '0').ToUpper();
                    OutputLine("正在发送-" + i.ToString());
                    if (string.IsNullOrEmpty(CurrentSNHex))
                    {
                        CurrentSNHex = hex;
                    }
                    SendCommand(btnGetImageXPackage, "040512-" + hex);
                    CurrentSNHex = hex;
                    //如果接收进程收到了信息，则继续
                    bool torf = autoResetEvent.WaitOne(ResponseWaitMilliSecondsAfterSentImage);
                    if (!torf)
                    {
                        autoResetEvent.Reset();
                    }
                }
                //发送完成停止
                HasGetFrameNStarted = false;
                //
                OutputLine("图像帧识别完成");
            }));
            t1.Start();
            //nudMaxFrameNo.Value++;
        }

        private void btnClear1_Click(object sender, EventArgs e)
        {
            richTextBox1.Clear();
        }

        private void btnClear2_Click(object sender, EventArgs e)
        {
            richTextBox2.Clear();
        }

        private void cbxIsAutoLoopGet_CheckedChanged(object sender, EventArgs e)
        {
            CheckBox cbx = sender as CheckBox;
            if (cbx.Checked && cbxLG220.SelectedIndex != -1)
            {
                string lgcode = cbxLG220.SelectedItem.ToString();
                LG220 lg220 = remoteLG220[lgcode];
                //foreach (LG220 lg220 in remoteLG220.Values)
                //{
                Thread t1 = new Thread(new ParameterizedThreadStart((obj) =>
                {
                    LG220 g220 = obj as LG220;
                    g220.Socket.SendTimeout = 5000;
                    try
                    {
                        while (IsAutoLoop())
                        {
                            foreach (L101 l101 in g220.NodeIds)
                            {
                                if (!IsAutoLoop())
                                {
                                    break;
                                }
                                SinglePhotography(g220.Socket, g220.GatewayId, l101.NodeId);
                            }
                        }
                    }
                    catch (Exception exc)
                    {
                        OutputLine(exc.Message);
                    }
                }));
                t1.Start(lg220);
                //}
            }
        }

        /// <summary>
        /// 命令发出后等待回应的最大时长
        /// </summary>
        int ResponseWaitMilliSecondsAfterSent = 5000;
        /// <summary>
        /// 获取图像命令发出后等待回应结果的最大时长
        /// </summary>
        int ResponseWaitMilliSecondsAfterSentImage = 20000;
        /// <summary>
        /// 拍照命令是否已发送，待回应
        /// </summary>
        private volatile bool HasPhotographyStarted = false;
        /// <summary>
        /// 查询拍照命令是否已发送，待回应
        /// </summary>
        private volatile bool HasPhotoQueryStarted = false;
        /// <summary>
        /// 识别拍照结果命令是否已发送，待回应
        /// </summary>
        private volatile bool HasPhotoDetectStarted = false;
        /// <summary>
        /// 查询识别结果命令是否已发送，待回应
        /// </summary>
        private volatile bool HasDetectQueryStarted = false;
        /// <summary>
        /// 获取图片首帧命令是否已发送，待回应
        /// </summary>
        private volatile bool HasGetFrameOneStarted = false;

        private void SinglePhotography(Socket socket, string gatewayid, string nodeid)
        {
            string hexTail;
            //
            if (autoResetEvent != null)
            {
                autoResetEvent.Close();
            }
            autoResetEvent = new AutoResetEvent(false);

            #region 前置命令
            //拍一张水表图
            hexTail = "02020201";
            SendCommand(socket, gatewayid, nodeid, hexTail, new Action<string>((hexComm) =>
            {
                HasPhotographyStarted = true;
                if (!autoResetEvent.WaitOne(ResponseWaitMilliSecondsAfterSent))
                {
                    autoResetEvent.Reset();
                }
            }));

            //查询拍照动作是否完成
            hexTail = "010102";
            SendCommand(socket, gatewayid, nodeid, hexTail, new Action<string>((hexComm) =>
            {
                HasPhotoQueryStarted = true;
                if (!autoResetEvent.WaitOne(ResponseWaitMilliSecondsAfterSent))
                {
                    autoResetEvent.Reset();
                }
            }));

            //命令识别图像
            hexTail = "02020300";
            SendCommand(socket, gatewayid, nodeid, hexTail, new Action<string>((hexComm) =>
            {
                HasPhotoDetectStarted = true;
                if (!autoResetEvent.WaitOne(ResponseWaitMilliSecondsAfterSent))
                {
                    autoResetEvent.Reset();
                }
                Thread.Sleep(3000);
            }));

            //查询识别动作是否成功完成
            hexTail = "010103";
            SendCommand(socket, gatewayid, nodeid, hexTail, null);
            HasDetectQueryStarted = true;
            if (!autoResetEvent.WaitOne(ResponseWaitMilliSecondsAfterSent))
            {
                autoResetEvent.Reset();
            }
            int x = 0;
            while (HasDetectQueryStarted && x < 8)
            {
                //不成功时至多再等2秒，分4次间隔，每个间隔0.5秒
                Thread.Sleep(500);
                //
                SendCommand(socket, gatewayid, nodeid, hexTail, null);
                if (!autoResetEvent.WaitOne(ResponseWaitMilliSecondsAfterSent))
                {
                    autoResetEvent.Reset();
                }
                x++;
            }
            if (HasDetectQueryStarted)
            {
                OutputLine("图像帧识别查询超时");
                return;
            }
            #endregion

            #region 查询首帧内容
            //清除缓存的首帧储存结果
            if (DicGatewayNodeImage.ContainsKey(gatewayid))
            {
                if (DicGatewayNodeImage[gatewayid].ContainsKey(nodeid))
                {
                    DicGatewayNodeImage[gatewayid].Remove(nodeid);
                }
            }
            //查询首帧内容
            hexTail = "040512-00000001";
            SendCommand(socket, gatewayid, nodeid, hexTail, new Action<string>((hexComm) =>
            {
                HasGetFrameOneStarted = true;
                if (!autoResetEvent.WaitOne(ResponseWaitMilliSecondsAfterSentImage))
                {
                    autoResetEvent.Reset();
                }
            }));
            //查询失败后，再试2次
            for (int i = 0; i < 2; i++)
            {
                if (!HasGetFrameOneStarted)
                    break;
                SendCommand(socket, gatewayid, nodeid, hexTail, new Action<string>((hexComm) =>
                {
                    HasGetFrameOneStarted = true;
                    if (!autoResetEvent.WaitOne(ResponseWaitMilliSecondsAfterSentImage))
                    {
                        autoResetEvent.Reset();
                    }
                }));
            }
            if (HasGetFrameOneStarted)
            {
                OutputLine("查询图片首帧失败退出");
                return;
            }
            #endregion

            #region 下载图像
            int start = 2;
            if (!DicGatewayNodeImage.ContainsKey(gatewayid) || !DicGatewayNodeImage[gatewayid].ContainsKey(nodeid)
                || DicGatewayNodeImage[gatewayid][nodeid] == null || DicGatewayNodeImage[gatewayid][nodeid].FirstFrame == null
                || string.IsNullOrEmpty(DicGatewayNodeImage[gatewayid][nodeid].FirstFrame.TailFrameSN))
            {
                OutputLine("未取到需要的图片首帧");
                return;
            }
            //尾帧
            int end = int.Parse(DicGatewayNodeImage[gatewayid][nodeid].FirstFrame.TailFrameSN, System.Globalization.NumberStyles.HexNumber);
            //剩余重试次数
            int retryNums = 3;
            //依次获取图像所有帧
            HasGetFrameNStarted = true;
            for (int i = start; i <= end; i++)
            {
                OutputLine("正在发送-" + i.ToString());
                //
                string hex = Convert.ToString(i, 16).PadLeft(8, '0').ToUpper();
                hexTail = "040512-" + hex;
                SendCommand(socket, gatewayid, nodeid, hexTail, new Action<string>((hexComm) =>
                {
                    CurrentSNHex = hex;
                    //如果接收进程收到了信息，则继续
                    bool torf = autoResetEvent.WaitOne(ResponseWaitMilliSecondsAfterSentImage);
                    if (!torf)
                    {
                        autoResetEvent.Reset();
                        if (retryNums > 0)
                        {
                            //后退一步，重新发送
                            i--;
                            retryNums--;
                            OutputLine("开始重试");
                        }
                        else
                        {
                            //如果重试3次仍然失败，退出本表图片接收
                            OutputLine("图像帧下载失败退出");
                            return;
                        }
                    }
                    else
                    {
                        retryNums = 3;
                    }
                }));
            }
            //发送完成停止
            HasGetFrameNStarted = false;
            #endregion 
            //
            OutputLine("图像帧识别完成");
        }

        private bool IsAutoLoop()
        {
            return GetCheckBoxChecked(cbxIsAutoLoopGet);
        }

        Func<CheckBox, bool> HandlerBool = null;
        private bool GetCheckBoxChecked(CheckBox cbx)
        {
            if (HandlerBool == null)
            {
                HandlerBool = new Func<CheckBox, bool>((cbox) =>
                {
                    return cbox.Checked;
                });
            }
            if (this.InvokeRequired)
            {
                object obj = this.Invoke(HandlerBool, cbx);
                return (bool)obj;
            }
            else
            {
                return cbx.Checked;
            }
        }

        private void btnShowResult_Click(object sender, EventArgs e)
        {
            string file = "detect_result.txt";
            ShowData(file);
            tabControl1.SelectedIndex++;
        }

        private void ShowData(string filePath)
        {
            //string file = "detect_result.txt";
            if (!File.Exists(filePath))
            {
                MessageBox.Show("还没有保存结果");
                return;
            }
            //
            DataTable dataTable = new DataTable(Guid.NewGuid().ToString());
            dataTable.Columns.Add("ImgPath");
            dataTable.Columns.Add("ImgNum");
            dataTable.Columns.Add("ImgDt");
            //
            string[] lines = File.ReadAllLines(filePath, Encoding.UTF8);
            //
            for (int i = 0; i < lines.Length; i++)
            {
                string[] temp = lines[i].Split(new char[] { '\t' });
                if (temp.Length >= 3)
                    dataTable.Rows.Add(temp[0], temp[1], temp[2]);
                else if (temp.Length == 2)
                    dataTable.Rows.Add(temp[0], temp[1], "");
                else if (temp.Length == 1)
                    dataTable.Rows.Add(temp[0], "", "");
            }
            //
            dataGridView1.DataSource = dataTable;
            dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
            //tabControl1.SelectedIndex = 1;
        }

        private void tsbReload_Click(object sender, EventArgs e)
        {
            string file = "detect_result.txt";
            string temp = tstbxFilePath.Text.Trim();
            if (!string.IsNullOrEmpty(temp))
            {
                if (Path.HasExtension(temp))
                {
                    file = temp;
                }
                else
                {
                    file = Path.Combine(temp, file);
                }
            }
            ShowData(file);
        }

        private void dataGridView1_RowStateChanged(object sender, DataGridViewRowStateChangedEventArgs e)
        {
            if (e.Row.Index >= 0)
            {
                string path = e.Row.Cells[0].Value.ToString();
                pictureBox1.ImageLocation = path;
            }
        }

        private void tsbFilter_Click(object sender, EventArgs e)
        {
            DataTable dataTable = dataGridView1.DataSource as DataTable;
            if (dataTable == null || dataTable.Rows.Count == 0) { return; }
            string nodeid = txtReaderCode.Text.Trim();
            StringBuilder sb = new StringBuilder();
            if (!string.IsNullOrEmpty(nodeid))
            {
                sb.Append($"ImgPath like '%-{nodeid}-%'");
            }
            if (dateTimePicker1.Checked)
            {
                string time = dateTimePicker1.Value.ToString("yyyy-MM-dd HH:mm:ss.fff");
                if (sb.Length > 0) { sb.Append(" and "); }
                sb.Append($"ImgDt >= '{time}'");
            }
            if (dateTimePicker2.Checked)
            {
                string time = dateTimePicker2.Value.ToString("yyyy-MM-dd HH:mm:ss.fff");
                if (sb.Length > 0) { sb.Append(" and "); }
                sb.Append($"ImgDt <= '{time}'");
            }
            if (sb.Length > 0)
            {
                dataTable.DefaultView.RowFilter = sb.ToString();
            }
            else
            {
                dataTable.DefaultView.RowFilter = "";
            }
        }

        private void btnSaveImgAs_Click(object sender, EventArgs e)
        {
            string path = txtImgSaveAs.Text.Trim();
            if (String.IsNullOrEmpty(path))
            {
                txtImgSaveAs.Text = System.IO.Path.Combine(tstbxFilePath.Text, "SelectedImg");
            }
            if (!Directory.Exists(txtImgSaveAs.Text.Trim()))
            {
                Directory.CreateDirectory(txtImgSaveAs.Text.Trim());
            }
            if (dataGridView1.Rows.Count <= 0) { return; }
            foreach (DataGridViewRow dgvr in dataGridView1.SelectedRows)
            {
                DataRowView drv = dgvr.DataBoundItem as DataRowView;
                string file = drv["ImgPath"].ToString();
                string newFile = Path.Combine(txtImgSaveAs.Text.Trim(), Path.GetFileName(file));
                File.Copy(file, newFile, true);
            }
        }

        private void cbxAttachDGVImg_CheckedChanged(object sender, EventArgs e)
        {
            CheckBox cbx = sender as CheckBox;
            if (cbx.Checked)
            {
                dataGridView1.RowStateChanged += dataGridView1_RowStateChanged;
            }
            else
            {
                dataGridView1.RowStateChanged -= dataGridView1_RowStateChanged;
            }
        }

        private void btnOpenSaveAs_Click(object sender, EventArgs e)
        {
            string pp = txtImgSaveAs.Text.Trim();
            if (!string.IsNullOrEmpty(pp))
            {
                Process.Start(pp);
            }
        }

        private void tsbOpenImg_Click(object sender, EventArgs e)
        {
            string xx = tstbxFilePath.Text.Trim();
            if (!string.IsNullOrEmpty(xx))
            {
                Process.Start(xx);
            }
        }

    }

    class LG220
    {
        public string GatewayId { get; set; }
        public Socket Socket { get; set; }
        public List<L101> NodeIds { get; set; }
    }

    class L101
    {
        public string NodeId { get; set; }
        public string NodeIdShort { get; set; }
    }

    class LG220Return
    {
        /// <summary>
        /// 版本 长度1byte eg:0x01
        /// </summary>
        public string Version { get; set; }
        /// <summary>
        /// 命令字 
        /// <para>长度 1 byte</para>
        /// <para>　主动上报数据 : 0x00--数据功能</para>
        /// <para>　被动轮询上报数据： 0x03--数据功能</para>
        /// <para>　终端模块入网请求： 0x01--集中器完成</para>
        /// <para>　终端模块校时请求： 0x02--集中器完成</para>
        /// </summary>
        public string Command { get; set; }

        /// <summary>
        /// 集中器ID 长度4byte eg:0x25A88D03
        /// </summary>
        public string GatewayId { get; set; }
        /// <summary>
        /// 节点ID 长度4byte eg:0x01010001
        /// </summary>
        public string NodeId { get; set; }
        /// <summary>
        /// 短ID 长度2byte  eg:0x0001
        /// </summary>
        public string NodeIdShort { get; set; }
        /// <summary>
        /// 通道 长度1byte  eg:0x02
        /// </summary>
        public string Tunnel { get; set; }
        /// <summary>
        /// SNR  长度1byte  eg:0x0D
        /// </summary>
        public string SNR { get; set; }
        /// <summary>
        /// RSSI   长度2byte  eg:0x1700
        /// </summary>
        public string RSSI { get; set; }
        /// <summary>
        /// NC    长度2byte  eg:0x0000
        /// </summary>
        public string NC { get; set; }
        /// <summary>
        /// 数据上报时 Unix 时间戳   长度4byte  eg:0x5DF8C9A3
        /// </summary>
        public string Timestamp { get; set; }
        /// <summary>
        /// 终端在线情况(0x01：掉线， 0x00：在线)  长度1byte  eg:0x00
        /// </summary>
        public string TerminalState { get; set; }
        public string TerminalStateName
        {
            get
            {
                if (TerminalState == "00")
                {
                    return "在线";
                }
                else
                {
                    return "离线";
                }
            }
        }
        /// <summary>
        /// 终端入网总数   长度2byte  eg:0x0001
        /// </summary>
        public string TerminalNums { get; set; }
        /// <summary>
        /// 数据长度    长度2byte   eg:0x0000
        /// </summary>
        public string DataLength { get; set; }
        /// <summary>
        /// 数据内容    
        /// </summary>
        public string DataContent { get; set; }
        /// <summary>
        /// 自定义协议之类别
        /// <code>1字节</code>
        /// <code>DataContent不空时有值</code>
        /// 0开头时表示请求帧
        /// 1开头时表示响应帧
        /// </summary>
        public string DiyProtecol { get; set; }
        /// <summary>
        /// 自定义协议之数据长度
        /// <code>DataContent不空时有值</code>
        /// <code>1字节</code>
        /// </summary>
        public string DiyDataLen { get; set; }
        /// <summary>
        /// 自定义协议之命令内容
        /// </summary>
        public string DiyCommand { get; set; }

        public string DiyCommandName { get; set; }
        public string GetDiyCommName()
        {
            if (!string.IsNullOrEmpty(DiyProtecol) && !string.IsNullOrEmpty(DiyDataLen) && !string.IsNullOrEmpty(DiyCommand))
            {
                StringBuilder sb = new StringBuilder();
                if (DiyProtecol == "81" && DiyDataLen == "02")
                {
                    string c1 = DiyCommand.Substring(0, 2);
                    string c2 = DiyCommand.Substring(2, 2);
                    if (c1 == "01")
                        sb.Append("系统运行状态");
                    else if (c1 == "02")
                        sb.Append("实时拍照完成状态");
                    else if (c1 == "03")
                        sb.Append("实时识别完成状态");
                    else if (c1 == "04")
                        sb.Append("模型接收完整状态");
                    else if (c1 == "05")
                        sb.Append("程序接收完整状态");
                    //
                    if (c2 == "01")
                        sb.Append("OK");
                    else if (c2 == "02")
                        sb.Append("NG");
                    else if (c2 == "03")
                        sb.Append("错误");
                    else if (c2 == "04")
                        sb.Append("其它1");
                    else if (c2 == "05")
                        sb.Append("其它2");
                    //
                    return sb.ToString();
                }
                else if (DiyProtecol == "82" && DiyDataLen == "02")
                {
                    string c1 = DiyCommand.Substring(0, 2);
                    string c2 = DiyCommand.Substring(2, 2);
                    if (c1 == "01")
                    {
                        sb.Append("补光灯");
                        if (c2 == "01")
                            sb.Append("灯1开灯2开");
                        else if (c2 == "02")
                            sb.Append("灯1开灯2关");
                        else if (c2 == "03")
                            sb.Append("灯1关灯2开");
                        else if (c2 == "04")
                            sb.Append("灯1关灯2关");
                    }
                    else if (c1 == "02")
                    {
                        sb.Append("实时拍照");
                        if (c2 == "01")
                            sb.Append("OK");
                        else if (c2 == "02")
                            sb.Append("忙碌");
                        else if (c2 == "03")
                            sb.Append("错误");
                    }
                    else if (c1 == "03")
                    {
                        sb.Append("识别回复");
                        if (c2 == "01")
                            sb.Append("OK");
                        else if (c2 == "02")
                            sb.Append("忙碌");
                        else if (c2 == "03")
                            sb.Append("错误");
                    }
                    //
                    return sb.ToString();
                }
            }
            return string.Empty;
        }
        /// <summary>
        /// 自定义协议之CRC16校验
        /// <code>DataContent不空时有值</code>
        /// <code>2字节</code>
        /// </summary>
        public string DiyCRC { get; set; }

        public bool IsImage()
        {
            if (!string.IsNullOrEmpty(DiyProtecol) && !string.IsNullOrEmpty(DiyDataLen)
                && DiyProtecol == "84" && DiyDataLen == "BA")
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// 是否拍照命令
        /// </summary>
        /// <returns></returns>
        public bool IsPhotography()
        {
            return DiyProtecol == "82" && DiyDataLen == "02" && DiyCommand.Substring(0, 2) == "02";
        }
        /// <summary>
        /// 是否查询拍照完成状态命令
        /// </summary>
        /// <returns></returns>
        public bool IsPhotoQuery()
        {
            return DiyProtecol == "81" && DiyDataLen == "02" && DiyCommand.Substring(0, 2) == "02";
        }
        /// <summary>
        /// 拍照命令状态是否成功完成
        /// </summary>
        /// <returns></returns>
        public bool IsPhotoQueryOK()
        {
            return DiyCommand.Substring(2, 2) == "01";
        }
        /// <summary>
        /// 是否识别图片命令
        /// </summary>
        /// <returns></returns>
        public bool IsPhotoDetect()
        {
            return DiyProtecol == "82" && DiyDataLen == "02" && DiyCommand.Substring(0, 2) == "03";
        }
        /// <summary>
        /// 是否 查询识别完成状态 命令
        /// </summary>
        /// <returns></returns>
        public bool IsDetectQuery()
        {
            return DiyProtecol == "81" && DiyDataLen == "02" && DiyCommand.Substring(0, 2) == "03";
        }
        /// <summary>
        /// 识别完成状态是否成功
        /// </summary>
        /// <returns></returns>
        public bool IsDetectQueryOK()
        {
            return DiyCommand.Substring(2, 2) == "01";
        }
        public override string ToString()
        {
            if (string.IsNullOrEmpty(DataContent))
            {
                return $"{Version}{Command}-{GatewayId}-{NodeId}-{NodeIdShort}-{Tunnel}-{SNR}{RSSI}{NC}{Timestamp}-{TerminalState}-{TerminalNums}-{DataLength}";
            }
            else
            {
                //return $"{Version}{Command}-{GatewayId}-{NodeId}-{NodeIdShort}-{Tunnel}-{SNR}{RSSI}{NC}{Timestamp}-{TerminalState}-{TerminalNums}-{DataLength}-{DataContent}";
                return $"{Version}{Command}-{GatewayId}-{NodeId}-{NodeIdShort}-{Tunnel}-{SNR}{RSSI}{NC}{Timestamp}-{TerminalState}-{TerminalNums}-{DataLength}-{DiyProtecol}-{DiyDataLen}-{DiyCommand}-{DiyCommandName}-{DiyCRC}";
            }
        }
    }

    class LG220ReturnImage : LG220Return
    {
        public LG220ReturnImage() { }
        public LG220ReturnImage(LG220Return orig)
        {
            this.Version = orig.Version;
            this.Command = orig.Command;
            this.GatewayId = orig.GatewayId;
            this.NodeId = orig.NodeId;
            this.NodeIdShort = orig.NodeIdShort;
            this.Tunnel = orig.Tunnel;
            this.SNR = orig.SNR;
            this.RSSI = orig.RSSI;
            this.Timestamp = orig.Timestamp;
            this.TerminalState = orig.TerminalState;
            this.TerminalNums = orig.TerminalNums;
            this.DataLength = orig.DataLength;
            this.DataContent = orig.DataContent;
            this.DiyProtecol = orig.DiyProtecol;
            this.DiyDataLen = orig.DiyDataLen;
            this.DiyCommand = orig.DiyCommand;
            this.DiyCRC = orig.DiyCRC;
            //
            if (orig.IsImage())
            {
                this.ImageDeviceType = orig.DiyCommand.Substring(0, 2);
                this.FrameSN = orig.DiyCommand.Substring(2, 8);
                this.FrameLength = orig.DiyCommand.Substring(10, 2);
                int len = int.Parse(this.FrameLength, System.Globalization.NumberStyles.HexNumber);
                this.FrameData = orig.DiyCommand.Substring(12, len * 2);
            }
        }

        /// <summary>
        /// 拍照设备类别
        /// <code>长度：1字节</code>
        /// <list type="number">
        /// <item>12:数字表</item>
        /// <item>13:指针表</item>
        /// </list>
        /// </summary>
        public string ImageDeviceType { get; set; }
        /// <summary>
        /// 帧序号
        /// <code>长度：4字节</code>
        /// </summary>
        public string FrameSN { get; set; }
        /// <summary>
        /// 帧数据长度 B4
        /// <code>1字节</code>
        /// </summary>
        public string FrameLength { get; set; }
        /// <summary>
        /// 帧数据
        /// <code>除了尾帧，其他帧长度：180字节</code>
        /// </summary>
        public string FrameData { get; set; }

        public bool IsFirstImage()
        {
            if (!string.IsNullOrEmpty(FrameSN) && FrameSN == "00000001")
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public override string ToString()
        {
            if (!base.IsImage())
            {
                return base.ToString();
            }
            else
            {
                StringBuilder sb = new StringBuilder($"{Version}{Command}-{GatewayId}-{NodeId}-{NodeIdShort}-{Tunnel}-{SNR}{RSSI}{NC}{Timestamp}-{TerminalState}-{TerminalNums}-{DataLength}");
                sb.Append($"-ImageDeviceType:{ImageDeviceType}");
                if (ImageDeviceType == "12")
                    sb.Append("数字表");
                else if (ImageDeviceType == "13")
                    sb.Append("指针表");
                //
                sb.Append($"-FrameSN:{FrameSN}-FrameLength:{FrameLength}-FrameData:{FrameData}");
                //
                return sb.ToString();
            }
        }
    }

    class LG220ReturnImageFirst : LG220ReturnImage
    {
        public LG220ReturnImageFirst() { }

        public LG220ReturnImageFirst(LG220ReturnImage orig)
        {
            this.Version = orig.Version;
            this.Command = orig.Command;
            this.GatewayId = orig.GatewayId;
            this.NodeId = orig.NodeId;
            this.NodeIdShort = orig.NodeIdShort;
            this.Tunnel = orig.Tunnel;
            this.SNR = orig.SNR;
            this.RSSI = orig.RSSI;
            this.Timestamp = orig.Timestamp;
            this.TerminalState = orig.TerminalState;
            this.TerminalNums = orig.TerminalNums;
            this.DataLength = orig.DataLength;
            this.DataContent = orig.DataContent;
            this.DiyProtecol = orig.DiyProtecol;
            this.DiyDataLen = orig.DiyDataLen;
            this.DiyCommand = orig.DiyCommand;
            this.DiyCRC = orig.DiyCRC;
            //
            this.ImageDeviceType = orig.ImageDeviceType;
            this.FrameSN = orig.FrameSN;
            this.FrameLength = orig.FrameLength;
            this.FrameData = orig.FrameData;
            //
            if (orig.IsFirstImage())
            {
                string time = orig.FrameData.Substring(0, 4) + "-" + orig.FrameData.Substring(4, 2) + "-" + orig.FrameData.Substring(6, 2)
                    + " " + orig.FrameData.Substring(8, 2) + ":" + orig.FrameData.Substring(10, 2) + ":" + orig.FrameData.Substring(12, 2)
                    + "." + orig.FrameData.Substring(14, 2) + "0";
                this.FrameTime = time;
                this.ImageByteNums = orig.FrameData.Substring(16, 8);
                this.HasDetected = orig.FrameData.Substring(24, 2);
                this.TailFrameSN = orig.FrameData.Substring(26, 8);
                this.ImageCRC32 = orig.FrameData.Substring(34, 8);
                this.AlgorithmVer = orig.FrameData.Substring(42, 4);
                this.ModelVer = orig.FrameData.Substring(46, 4);
                this.DigitLen = orig.FrameData.Substring(50, 2);
                int len = int.Parse(this.DigitLen, System.Globalization.NumberStyles.HexNumber);
                this.DigitHex = orig.FrameData.Substring(52, len * 2);
                for (int i = 0; i < len; i++)
                {
                    this.DigitHuman += (char)int.Parse(this.DigitHex.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
                }
            }
            else
            {
                this.FrameTime = string.Empty;
                this.ImageByteNums = string.Empty;
                this.HasDetected = string.Empty;
                this.TailFrameSN = string.Empty;
                this.ImageCRC32 = string.Empty;
                this.AlgorithmVer = string.Empty;
                this.ModelVer = string.Empty;
                this.DigitLen = string.Empty;
                this.DigitHex = string.Empty;
            }
        }

        /// <summary>
        /// 首帧中的日期时间
        /// <code>8字节</code>
        /// <list type="number">
        /// <item>年左2、年右2、月、日、时、分、秒、10毫</item>
        /// </list>
        /// </summary>
        public string FrameTime { get; set; }
        /// <summary>
        /// 图片总字节数
        /// <code>4字节</code>
        /// </summary>
        public string ImageByteNums { get; set; }
        /// <summary>
        /// 已识别
        /// <code>1字节</code>
        /// <list type="bullet">
        /// <item><code>01</code><description>未识别</description></item>
        /// <item><code>02</code><description>已识别</description></item>
        /// </list>
        /// </summary>
        public string HasDetected { get; set; }
        /// <summary>
        /// 尾帧序号
        /// <code>4字节</code>
        /// </summary>
        public string TailFrameSN { get; set; }
        /// <summary>
        /// 图像的CRC32
        /// <code>4字节</code>
        /// </summary>
        public string ImageCRC32 { get; set; }
        /// <summary>
        /// 算法版本号
        /// <code>2字节</code>
        /// </summary>
        public string AlgorithmVer { get; set; }
        /// <summary>
        /// 模板版本号
        /// <code>2字节</code>
        /// </summary>
        public string ModelVer { get; set; }
        /// <summary>
        /// 识别出的数字占用的字节数
        /// <code>1字节</code>
        /// </summary>
        public string DigitLen { get; set; }
        /// <summary>
        /// 识别出的结果的HEX
        /// <code>长度参见DigitLen</code>
        /// </summary>
        public string DigitHex { get; set; }
        /// <summary>
        /// 可阅读的识别结果
        /// </summary>
        public string DigitHuman { get; set; }

        public override string ToString()
        {
            if (!base.IsFirstImage())
            {
                return base.ToString();
            }
            else
            {
                StringBuilder sb = new StringBuilder($"{Version}{Command}-{GatewayId}-{NodeId}-{NodeIdShort}-{Tunnel}-{SNR}{RSSI}{NC}{Timestamp}-{TerminalState}-{TerminalNums}-{DataLength}");
                sb.Append($"-帧别:{ImageDeviceType}");
                if (ImageDeviceType == "12")
                    sb.Append("数字表");
                else if (ImageDeviceType == "13")
                    sb.Append("指针表");
                //
                sb.Append($"-帧序:{FrameSN}-帧长:{FrameLength}-帧时间:{FrameTime}-图片大小:{ImageByteNums}-HasDetected:{HasDetected}-尾帧:{TailFrameSN}-ImageCRC32:{ImageCRC32}-AlgorithmVer:{AlgorithmVer}-ModelVer:{ModelVer}-DigitLen:{DigitLen}-识别结果:{DigitHex}");
                //
                return sb.ToString();
            }
        }
    }

    class LG220Image
    {
        public LG220ReturnImageFirst FirstFrame { get; set; }
        public List<LG220ReturnImage> OtherFrames { get; set; }
        private bool IsOtherFrameSorted = false;

        public bool IsAllFrameReceived()
        {
            if (FirstFrame == null) return false;
            if (OtherFrames.Count == 0) return false;
            int lastSN = int.Parse(FirstFrame.TailFrameSN, System.Globalization.NumberStyles.HexNumber);
            //检查总数是否足够
            if (lastSN - 1 != OtherFrames.Count)
            {
                return false;
            }
            //数据帧中存在尾帧序号了，表明图片帧收齐了
            bool torf = OtherFrames.Exists(x => x.FrameSN == FirstFrame.TailFrameSN);
            return torf;
        }

        public void SortOtherFrame()
        {
            if (!IsOtherFrameSorted)
            {
                //按FrameSN排序
                OtherFrames.Sort(new Comparison<LG220ReturnImage>((e1, e2) =>
                {
                    int sn1 = int.Parse(e1.FrameSN, System.Globalization.NumberStyles.HexNumber);
                    int sn2 = int.Parse(e2.FrameSN, System.Globalization.NumberStyles.HexNumber);

                    return sn1.CompareTo(sn2);
                }));
                //
                IsOtherFrameSorted = true;
            }
        }

        public bool IsImageFramesOK()
        {
            string imgCrc32 = FirstFrame.ImageCRC32;
            int lastSN = int.Parse(FirstFrame.TailFrameSN, System.Globalization.NumberStyles.HexNumber);
            //检查总数是否足够
            if (lastSN - 1 != OtherFrames.Count)
            {
                return false;
            }
            //
            SortOtherFrame();
            //检查顺序是否连续
            for (int i = 0; i < OtherFrames.Count; i++)
            {
                LG220ReturnImage mod = OtherFrames[i];
                int sn = int.Parse(mod.FrameSN, System.Globalization.NumberStyles.HexNumber);
                if (sn - 2 != i)
                {
                    return false;
                }
            }
            //检查图片的CRC32是否OK
            ImageBytes = GetImageBytes();
            //
            string crcdata = CRC.CRC32(ImageBytes);
            return (crcdata == imgCrc32);
        }

        public byte[] GetImageBytes()
        {
            int imgByteLength = int.Parse(FirstFrame.ImageByteNums, System.Globalization.NumberStyles.HexNumber);
            byte[] data = new byte[imgByteLength];
            //
            SortOtherFrame();
            //
            for (int i = 0; i < OtherFrames.Count; i++)
            {
                LG220ReturnImage mod = OtherFrames[i];
                int sn = int.Parse(mod.FrameSN, System.Globalization.NumberStyles.HexNumber) - 1;
                int frameLen = int.Parse(mod.FrameLength, System.Globalization.NumberStyles.HexNumber);
                //
                byte[] frameData = UtilityHelper.Hex2Bytes(mod.FrameData);
                //从上一帧的末尾开始拷贝
                Array.Copy(frameData, 0, data, 180 * (sn - 1), frameLen);
            }
            //
            return data;
        }

        public byte[] ImageBytes { get; set; }
    }
}